use crate::object::Object;

pub fn process(object_list: &mut [Object]) {
    for current_object_index in 0..object_list.len() {
        let other_object_index_list = object_list
            .iter()
            .enumerate()
            .map(|(idx, _)| idx)
            .filter(|&idx| idx != current_object_index)
            .collect::<Vec<usize>>();

        let in_sensor_range_other_object_index_list = other_object_index_list
            .iter()
            .copied()
            .filter(|&other_object_index| {
                let distance = nalgebra::distance(
                    &object_list[current_object_index].cell.position,
                    &object_list[other_object_index].cell.position,
                );
                distance <= object_list[current_object_index].cell.sensor.range
            })
            .collect::<Vec<usize>>();

        // set sensor.data_list by energy and position
        // danger: -1.0 * other_object_energy / distance
        // equal: -0.5 * other_object_energy / distance
        // normal: 0.0 * other_object_energy / distance
        // food: 1.0 * other_object_energy / distance
        // [up, right, down, left]
        let mut sensor_data_list = vec![0.0; 4];
        for other_object_index in in_sensor_range_other_object_index_list {
            let distance = nalgebra::distance(
                &object_list[current_object_index].cell.position,
                &object_list[other_object_index].cell.position,
            );

            let current_object_energy = object_list[current_object_index].cell.energy;
            let other_object_energy = object_list[other_object_index].cell.energy;

            let status = match current_object_energy {
                _ if other_object_energy > current_object_energy => -1.0,
                _ if other_object_energy == current_object_energy => -0.5,
                _ if other_object_energy < current_object_energy => 1.0,
                _ => 0.0
            } * other_object_energy as f64 / distance;

            let current_object_position = object_list[current_object_index].cell.position;
            let other_object_position = object_list[other_object_index].cell.position;

            // up
            if other_object_position.y < current_object_position.y {
                sensor_data_list[0] += status;
            }
            // right
            if other_object_position.x > current_object_position.x {
                sensor_data_list[1] += status;
            }
            // down
            if other_object_position.y > current_object_position.y {
                sensor_data_list[2] += status;
            }
            // left
            if other_object_position.x < current_object_position.x {
                sensor_data_list[3] += status;
            }
        }
        object_list[current_object_index].cell.sensor.data_list = sensor_data_list;
    }
}

#[cfg(test)]
mod tests {
    mod process {
        mod danger_rd {
            #[test]
            fn test() {
                let mut rng = rand::thread_rng();
                let mut object_1 = crate::object::Object::new(&mut rng);
                object_1.cell.position.x = 0.0;
                object_1.cell.position.y = 0.0;
                object_1.cell.energy = 1;
                let mut object_2 = crate::object::Object::new(&mut rng);
                object_2.cell.position.x = 0.1;
                object_2.cell.position.y = 0.1;
                object_2.cell.energy = 2;
                let mut object_list = vec![object_1, object_2];
                crate::system::sensor::process(&mut object_list);
                assert!(object_list[0].cell.sensor.data_list[0] == 0.0);
                assert!(object_list[0].cell.sensor.data_list[1] < 0.0);
                assert!(object_list[0].cell.sensor.data_list[2] < 0.0);
                assert!(object_list[0].cell.sensor.data_list[3] == 0.0);

                assert!(object_list[1].cell.sensor.data_list[0] > 0.0);
                assert!(object_list[1].cell.sensor.data_list[1] == 0.0);
                assert!(object_list[1].cell.sensor.data_list[2] == 0.0);
                assert!(object_list[1].cell.sensor.data_list[3] > 0.0);
            }
        }
    }
}